////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Shader extension
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   fragLib.cfi
//  Version:     v1.00
//  Created:     10/09/2006 by Tiago Sousa
//  Compilers:   
//  Description: Common fragment program - unified shading across CryEngine2
//
// -------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////

//////////////////////////////// Shared fragment shader /////////////////////////

/*

struct fragPass       (in shadeLib.cfi)
struct fragLightPass  (in shadeLib.cfi)


frag_unify_parameters( inout fragPass pPass )
  - Where user unifies parameters (from tweakables, etc) and sets flags (like bRenormalizeNormal)

frag_custom_begin(in vert2FragGeneral IN, inout fragCustomPass pPass)  
  - Customized initialization like fetching required textures, do pre-multiplications/computations, etc.  
  - This is specific for a rendering pass
  - In fragPass structure, user can add custom data (like for ex reflection vector, reflection color,
  or some constant term like fFresnel)
  
frag_custom_ambient(in fragCustomPass pPass, inout half3 cAmbient ) 
  - Customized ambient pass, for adding reflections or diferent ambient terms, etc
  - cAmbient at input is ambient color (or spherical harmonics ambient term)
  - User can apply diffuse texture or some custom computation (for example, cloth uses a fuzzy term for ambient instead)

frag_custom_per_light(in fragCustomPass pPass, inout fragLightPass pLight)
  - Custom shading computations
  - This is executed once per-light source
  - User can add custom per-light data into fragLightPass structure  
  
void frag_final_composition( inout fragPass pPass, inout half3 cFinal )
  - shared final shading composition
    
frag_custom_end(in vert2FragGeneral IN, in fragCustomPass pPass, inout half3 cOut)
  - Customized pass finalization (like applying vertex colors, alphaglow, etc)
    
. Check HumanSkin.cfx shader for simple usage example
  
*/

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 matViewProjI : PB_ViewProjMatrix_I;

void frag_unify(inout fragPass pPass, in vert2FragGeneral IN)
{
  // Set quality level (LOW or HIGH)
  pPass.nQuality = GetShaderQuality();

	// Set fresnel default values
  pPass.fFresnel_Bias = 1.0h;
  pPass.fFresnel_Scale = 0.0h;
	pPass.fFresnel_Pow = 4.0h;

  pPass.IN.baseTC = IN.baseTC;
  pPass.IN.bumpTC = pPass.IN.baseTC;

#if %_RT_FOG ||%_RT_DECAL_TEXGEN_2D || %DECAL
  pPass.IN.Ambient = IN.Ambient;
#else
  pPass.IN.Ambient = 1.0;
#endif

  pPass.IN.vTangent = half4(1,0,0,1);
  pPass.IN.vBinormal = half4(0,1,0,1);
  pPass.IN.vNormal.xyz = half4(0,0,1,1);

#if %_RT_FOG || %ANISO_SPECULAR || %MULTILAYER || %TEMP_EYES || %OFFSETBUMPMAPPING || %PARALLAX_OCCLUSION_MAPPING
  pPass.IN.vTangent = IN.vTangent;
  pPass.IN.vBinormal = IN.vBinormal;
  pPass.IN.vNormal.xyz = (cross(IN.vTangent.xyz, IN.vBinormal.xyz)) * IN.vTangent.w;                  // 4 alu
#endif

#if VS_TESSELLATION && !_RT_NO_TESSELLATION
  // here we reconstruct screenProj and vView from HPosition instead of reading DS output attributes
  float4 vTmp = IN.HPosition;
  vTmp.xy *= PS_ScreenSize.zw * 2;
  float4 vTmp1 = vTmp;
  vTmp.xyz *= IN.HPosition.w;
  pPass.IN.screenProj = vTmp;

  vTmp1.xy = vTmp1.xy * 2 - 1;
  vTmp1.y *= -1;
  vTmp1.xyz *= IN.HPosition.w;
  vTmp1 = mul(matViewProjI, vTmp1);
  vTmp1 /= vTmp1.w;
  vTmp1.xyz -= PS_WorldViewPos.xyz;
  pPass.IN.vView = float4(vTmp1.xyz, IN.vView.w);
#else
  pPass.IN.screenProj = IN.screenProj;
  pPass.IN.vView = IN.vView;
#endif

#if %_LT_LIGHTS && %_LT_HASPROJ  
  pPass.IN.projTC = IN.projTC;
#endif    

#if %VERTCOLORS || %DIRTLAYER || %BLENDLAYER
  pPass.IN.Color = IN.Color;
#endif

#if %_RT_DECAL_TEXGEN_2D
  pPass.IN.DistAtten = IN.DistAtten;
#elif %_RT_FOG
  pPass.IN.AvgFogVolumeContrib = IN.AvgFogVolumeContrib;
#endif

#if %_RT_MSAA_SAMPLEFREQ_PASS
	pPass.IN.uSample = IN.uSample;
#endif

  frag_unify_parameters( pPass );     
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void frag_final_composition( inout fragPass pPass, inout half3 cFinal )
{  
  half3 cDiffuse = pPass.cDiffuseMap.xyz;
  if( !pPass.bSkipMaterial)
    cDiffuse.xyz *= MatDifColor.xyz;
    
  half3 cSpecularCol = MatSpecColor.xyz;
#if !%_RT_FOG
	ComputeSnowBlending(cDiffuse, pPass.cSnowMap, pPass.cDiffuseMap.xyz);
	if(SnowVolumeParams[0].x)
		cSpecularCol = lerp(cSpecularCol, 0.3f, pPass.cSnowMap.w);
#endif

#if !%_RT_FOG
  half4 cDecalsMap = tex2Dproj(decalsAccSampler, pPass.IN.screenProj.xyzw );
	cDecalsMap.rgb *= cDecalsMap.rgb;
  cDiffuse.xyz *= (1.0f-cDecalsMap.w);
  cDiffuse.xyz += cDecalsMap.xyz; //* cDecalsMap.w;
#endif

  cDiffuse *= ( pPass.cAmbientAcc.xyz + pPass.cDiffuseAcc.xyz ); //3 alu

  if( pPass.nReflectionMapping )
  {
    // Apply shading to custom environment map:
    //  - MatDifColor.w contains either 0 or 1 depending if pick nearest cubemap functionality used or not
    //  - Would be good to get rid of this extra instructions, if no custom cubemaps used at some point in time

    half3 cEnvShading = lerp( 1, ( pPass.cAmbientAcc.xyz + pPass.cDiffuseAcc.xyz ), MatDifColor.w );

    pPass.cSpecularAcc.xyz += pPass.cEnvironment.xyz * cEnvShading;                                 // 1 alu
  }
  
  half3 cSpecular = pPass.cSpecularAcc.xyz * pPass.cGlossMap.xyz;                    // 2 alu
  if( !pPass.bSkipMaterial)
    cSpecular.xyz *= cSpecularCol.xyz;
  
  // Apply fresnel to reflections
  cSpecular.xyz *= pPass.fFresnel;
  
  cFinal.xyz += cDiffuse;
  cFinal.xyz += cSpecular;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void frag_quality_setup( inout fragPass pPass )
{
  // Set quality level (LOW, MEDIUM, HIGH or MAX)
  pPass.nQuality = GetShaderQuality();

  // Check quality settings and disable stuff 

  if( pPass.nQuality == QUALITY_LOW )
  {
    pPass.bRenormalizeNormal = pPass.bForceRenormalizeNormal;
    pPass.nReflectionMapping = false;
    pPass.bDetailBumpMapping = false;
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setup final fog. Per-vertex for alpha blended objects and per-object volumetric fog

void frag_fog_setup( inout fragPass pPass, inout half4 cOut)
{
#if %_RT_FOG && !%_RT_DECAL_TEXGEN_2D
  ComputeGlobalFogPS(cOut.xyz, pPass.IN.vView.w);

  cOut.xyz = pPass.IN.AvgFogVolumeContrib.xyz + cOut.xyz * pPass.IN.AvgFogVolumeContrib.w;
  cOut.w *= pPass.IN.vView.w;
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void frag_get_deferred_buffers( inout fragPass pPass )
{
	pPass.cShadowOcclMap = 0;

  // Alpha blend processed as regular forward rendering
  #if !%_RT_FOG
		float2 tcProj = pPass.IN.screenProj.xy / pPass.IN.screenProj.w;

    #if !XENON && !PS3 && !CAFE
      #if %_RT_MSAA_QUALITY || %_RT_MSAA_QUALITY1
        pPass.fSceneDepthRT = GetDecodedSceneDepthMS(tcProj, GetMSAASampleNum(), pPass.IN.uSample );
        pPass.vSceneUVsRT = DecodeSceneUV( GetSceneUVsMS(tcProj, GetMSAASampleNum(), pPass.IN.uSample ).x );
      #else
        pPass.fSceneDepthRT = DecodeSceneDepth(sceneDepthSampler, pPass.IN.screenProj.xyww);
        pPass.vSceneUVsRT = DecodeSceneUV( tex2Dproj(sceneUVsSampler, pPass.IN.screenProj.xyww).x );
      #endif
    #endif

		#if %_RT_MSAA_QUALITY || %_RT_MSAA_QUALITY1
			pPass.cNormalMapRT = GetSceneNormalsMS( tcProj, GetMSAASampleNum(), pPass.IN.uSample );
		#else
			pPass.cNormalMapRT = GetWorldSpaceNormals(tex2Dproj( sceneNormalsSampler, pPass.IN.screenProj.xyzw), true);
		#endif

		#if %_LT_LIGHTS
			pPass.cShadowOcclMap = pPass.cNormalMapRT.w;
		#endif

    #if %_RT_AMBIENT
			#if %_RT_MSAA_QUALITY || %_RT_MSAA_QUALITY1
				pPass.cDiffuseAccRT = GetSceneDiffuseAccMS( tcProj, GetMSAASampleNum(), pPass.IN.uSample ) * PS_HDR_RANGE_ADAPT_LBUFFER_MAX;
			#else
				pPass.cDiffuseAccRT = DecodeLightBuffer( tex2Dproj( sceneDiffuseAccSampler, pPass.IN.screenProj.xyzw) ) * PS_HDR_RANGE_ADAPT_LBUFFER_MAX; 
			#endif

      #if !%_RT_SHADER_LOD 
        if( pPass.bDeferredSpecularShading )
				{
					#if %_RT_MSAA_QUALITY || %_RT_MSAA_QUALITY1
						pPass.cSpecularAccRT = GetSceneSpecularAccMS( tcProj, GetMSAASampleNum(), pPass.IN.uSample ) * PS_HDR_RANGE_ADAPT_LBUFFER_MAX;
					#else
						pPass.cSpecularAccRT = DecodeLightBuffer( tex2Dproj( sceneSpecularAccSampler, pPass.IN.screenProj.xyzw) ); 
					#endif
				}
      #endif

      pPass.cDiffuseAcc = pPass.cDiffuseAccRT;
      pPass.cSpecularAcc = pPass.cSpecularAccRT;

    #endif
  #endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void frag_hdr_setup(inout fragPass pPass, inout half4 cOut)
{
  // Re-scale range
  if( !pPass.bSkinIrradiancePass )
    cOut.xyz *= PS_HDR_RANGE_ADAPT_MAX;

#if PS3
   #if !%_RT_FOG && %_RT_HDR_MODE

    bool bMultipassAlphaBlend = false;
    bool bMultipassAdditiveBlend = false;
    #if TEMP_TERRAIN || %DECAL
      bMultipassAlphaBlend = true;
    #endif
    #if !%_RT_AMBIENT
      bMultipassAdditiveBlend = true;
    #endif
    
    if ( bMultipassAlphaBlend || bMultipassAdditiveBlend )
    {
      // Custom blending for PS3 (decode and blend)
      half4 cDstRT = DecodeHDRBuffer( tex2Dproj( HDRTargetEncodedSampler, pPass.IN.screenProj.xyzw ) ); 
      if( bMultipassAlphaBlend )
        cOut = lerp( cDstRT, cOut, cOut.w );
      if( bMultipassAdditiveBlend )
        cOut += cDstRT;
    }

    cOut = EncodeHDRBuffer( cOut );

  #endif
#endif

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void frag_ambient( inout fragPass pPass, half3 vNormal )
{

#if %_RT_AMBIENT
	
	#if %_RT_FOG

		half3 amb = pPass.IN.Ambient.xyz;    

		if( pPass.bHemisphereLighting )  
		{
			half fBlendFactor = (vNormal.z*0.25h+0.75h); // 1 inst
			amb.xyz *= fBlendFactor; // 1 inst
		}

		// custom ambient pass
		frag_custom_ambient(pPass, amb);

	#else

		// we still need to separately add emissive color in case of deferred ambient
		if(pPass.bDontUseEmissive == false)
			pPass.cAmbientAcc.xyz += MatEmissiveColor.xyz;

	#endif

#endif  // _RT_AMBIENT

}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

half GetBlendedHeight(float2 uv, half blendLayerFactor, in float blendLayer2Tiling)
{
	half h = tex2D(bumpHeightMapSampler, uv).w;
#if %BLENDLAYER
	h += blendLayerFactor * (tex2D(HeightMap2Sampler, uv * blendLayer2Tiling).w - h);
#endif
	return h;
}

half4 frag_shared_output(inout fragPass pPass)
{ 
  half4 cOut = 0;     

  frag_quality_setup( pPass );
  frag_get_deferred_buffers( pPass );

#if TEMP_TERRAIN
	const float2 microDetailBaseTC = pPass.IN.bumpTC.xy;
	const float dispAmount = pPass.fBumpHeightScale * pPass.IN.Color.g;
#else
	const float2 microDetailBaseTC = pPass.IN.baseTC.xy;
	const float dispAmount = pPass.fBumpHeightScale;
#endif

	float blendLayer2Tiling = 0;
#if %BLENDLAYER
#ifdef CAFE
	pPass.blendFac = GetLayerBlendingValue(BlendMapSampler, microDetailBaseTC, 0, pPass.IN.Color.a, BlendFactor, BlendFalloff, false);
#else
	pPass.blendFac = GetLayerBlendingValue(BlendMapSampler, microDetailBaseTC, 0, pPass.IN.Color.a, BlendFactor, BlendFalloff);
#endif
	blendLayer2Tiling = BlendLayer2Tiling;
#endif

  pPass.vView = normalize(-pPass.IN.vView.xyz);                                                           // 3 alu  
  pPass.mTangentToWS = float3x3(pPass.IN.vTangent.xyz, pPass.IN.vBinormal.xyz, pPass.IN.vNormal.xyz);  

  if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_OBM)
  {
    const half3 viewDir = mul(pPass.mTangentToWS, pPass.vView);
    const float2 newCoords = OffsetMap(microDetailBaseTC, viewDir, 2, dispAmount, pPass.fHeightBias, pPass.blendFac, blendLayer2Tiling);

    pPass.IN.baseTC.xy = newCoords;
    pPass.IN.bumpTC.xy = newCoords;
  }

	if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_POM)
  {
    float2 ddxMicroDetailBaseTC, ddyMicroDetailBaseTC;
    float lod = ComputeTextureLOD(microDetailBaseTC, ResInfoBump.xy, ddxMicroDetailBaseTC, ddyMicroDetailBaseTC);

  #if TEMP_TERRAIN || %DECAL || %_RT_FOG
    if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_POM)
    {
      const float3 viewDir = mul(pPass.mTangentToWS, -pPass.vView);
      const float2 offsetBest = ParallaxOcclusionMap(microDetailBaseTC, lod, viewDir, 15, dispAmount, pPass.fHeightBias, pPass.blendFac, blendLayer2Tiling);

      pPass.IN.baseTC.xy = offsetBest.xy;
      pPass.IN.bumpTC.xy = offsetBest.xy;
    }
  #else

    if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_POM)
    {
      const float3 viewDirWS = pPass.IN.vView.xyz;
      const float3 parallax = mul(pPass.mTangentToWS, pPass.vView).xyz;

      const float sceneDepth = pPass.fSceneDepthRT;
      const float scale = sceneDepth - dot(viewDirWS, -CamFrontVector_POM);
      const float2 offset = normalize( parallax.xy / -parallax.z ) * scale * 0.25;

      pPass.IN.baseTC.xy = microDetailBaseTC + offset.xy;
      pPass.IN.bumpTC.xy = microDetailBaseTC + offset.xy;
    }
    else
    {
      // Reminder: we still have to figure out solution to output sv_coverage mask in gbuffer generation for msaa to work on clipped regions
      const float2 sceneUVs = pPass.vSceneUVsRT; 

      //if (abs(sceneUVs.x - 7.5f) > 1e-04)
        //discard;
      //if (abs(sceneUVs.y + 7.5f) > 1e-04)
        //discard;
      pPass.IN.baseTC.xy = sceneUVs;
      pPass.IN.bumpTC.xy = sceneUVs;
    }

  #endif
        
    if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_POM)
    {
      // high quality lookup which works with anisotropic filtering
      pPass.cDiffuseMap = GetTexture2DGrad(diffuseMapSampler, pPass.IN.baseTC.xy, ddxMicroDetailBaseTC, ddyMicroDetailBaseTC);
      if (!pPass.bDontUseBump)
        pPass.cBumpMap.xyz = GetNormalMapGrad(bumpMapSampler, pPass.IN.bumpTC.xy, ddxMicroDetailBaseTC, ddyMicroDetailBaseTC);
      else
        pPass.cBumpMap = half3(0,0,1);
    }
    else
    {
      pPass.cDiffuseMap = tex2Dlod(diffuseMapSampler, float4(pPass.IN.baseTC.xy, 0, 0)); // TODO: find way to compute lod without gradients as they cause pixel artifacts at fin / shell transition
    }
  }
  else
  {
    pPass.cDiffuseMap = tex2D(diffuseMapSampler, pPass.IN.baseTC.xy);
    if (!pPass.bDontUseBump)
      pPass.cBumpMap = GetNormalMap(bumpMapSampler, pPass.IN.bumpTC.xy);                                      // 2 alu 
    else
      pPass.cBumpMap = half3(0,0,1);	
  }

  pPass.vNormalDiffuse = pPass.cBumpMap; 

  //#if !TEMP_TERRAIN
  half2 vDetailTC = pPass.vDetailBumpTilling * pPass.IN.baseTC.xy;
  //#else
  //half2 vDetailTC = pPass.vDetailBumpTilling; // terrain texcoords are cam position dependent - so we need to handle detail tex coord in a special way (see vs)
  //#endif

  half4 cDetailMap = tex2D(detailMapSampler, vDetailTC);				// 1 tex, 1 mul - compiler optimizes away when not used
  if( pPass.bDetailBumpMapping )
  {
    if( pPass.bDetailBumpMappingMasking )
    {
      pPass.fDetailBumpScale *= pPass.cDiffuseMap.w;
      pPass.vDetailBlendAmount *= pPass.cDiffuseMap.w;
    }
    			    			
    // Detail bump
    #if %DETAIL_TEXTURE_IS_NORMALMAP
		half2 vDetailN = GetNormalMapFromTextureColor(cDetailMap).xy;
	#else
		half2 vDetailN = EXPAND(cDetailMap).xy;
	#endif
	
		pPass.cBumpMap.xy += vDetailN.xy * pPass.fDetailBumpScale;			                                 // 1 mad
  }

  // Set default alpha
  pPass.fAlpha = pPass.cDiffuseMap.w * pPass.IN.Ambient.w;

   // Debug output
#if %_RT_DEBUG0 || %_RT_DEBUG1 || %_RT_DEBUG2 || %_RT_DEBUG3
   DebugOutput(cOut, pPass.IN.baseTC);
   return cOut;
#endif

  //if %_RT_DEFERRED_SHADING && !%ENVIRONMENT_MAP
  #if !%_RT_FOG
  
    pPass.vNormal.xyz = pPass.cNormalMapRT.xyz;

  #else
    //  // Get main vectors/coeficients	     
    pPass.vNormal = mul(pPass.cBumpMap.xyz, pPass.mTangentToWS);                                      // 3 alu
		
		if (pPass.bRenormalizeNormal)
      pPass.vNormal = normalize(pPass.vNormal);                                                       // 3 alu
  #endif
 
  // Store some constant coeficients
  pPass.fNdotE = ( dot(pPass.vView.xyz, pPass.vNormal.xyz));                                        // 1 alu
	pPass.fGloss = MatSpecColor.w;
  pPass.vReflVec = (2.0h * pPass.fNdotE * pPass.vNormal.xyz) - pPass.vView.xyz;                      // 2 alu  		    

	// Set fresnel
	pPass.fFresnel = pPass.fFresnel_Bias + pPass.fFresnel_Scale * pow(1.001h - saturate(pPass.fNdotE), pPass.fFresnel_Pow); // 4 inst
                                                                                          
  // do custom pass setup
  frag_custom_begin(pPass);

	if(SnowVolumeParams[0].x)
	{
		pPass.cSnowMap = tex2Dlod(sceneSnowMapSampler, half4(pPass.IN.screenProj.xy/pPass.IN.screenProj.w,0,0));
		pPass.fGloss = lerp(pPass.fGloss, 0.3f, pPass.cSnowMap.w);
	}
	
  if( pPass.bDetailBumpMapping )
  {
    // Diffuse/gloss detail: lerp(0.5, dm, amount) * base * 2 for both diffuse and gloss
		half2 vBlendAmount = lerp(0.5, cDetailMap.zw, pPass.vDetailBlendAmount) * 2.0;
		pPass.cDiffuseMap.xyz *= vBlendAmount.x;																																	 // 2 muls
	  pPass.cGlossMap.xyz *= vBlendAmount.y; 
  }
	else // support licensees who wants the old detail map behaviour. see old (3.3.7 ???) shaders
	{
#if %DETAIL_TEXTURE_IS_SET && !%_RT_SHADER_LOD && !TEMP_TERRAIN
		half fLerp = length(pPass.IN.vView.xyz / DetailParams.w / 0.8);

		if( pPass.bDetailBumpMappingMasking )
		{
			fLerp /= pPass.cDiffuseMap.w + 0.001;
		}

		half3 detColor = lerp(cDetailMap.xyz, 0.5, saturate(fLerp));

		pPass.cDiffuseMap.xyz *= 2*detColor;
#endif
	}

  // load the environment map
  if( pPass.nReflectionMapping > 0 )
  {
    if( pPass.nReflectionMapping == REFLECT_CUBE)
    {
      pPass.cEnvironment = GetEnvironmentCMap(envMapSamplerCUBE, pPass.vReflVec.xyz, pPass.fGloss).xyz;    
    }
    else
    if( pPass.nReflectionMapping == REFLECT_SPHERE)
    {
      // should be transformed to view space - but adds quite a lot instructions
      pPass.cEnvironment = GetEnvironment2DMap(envMapSampler, pPass.vReflVec.xy);      
    }
  }

  half3 vAmbientNormal = pPass.vNormal.xyz;

#if %_RT_DEBUG0

	pPass.cDiffuseMap.xyz = 1;
	if (pPass.bAlphaGlow)
		pPass.cDiffuseMap.w = 0;
	
	pPass.cGlossMap.xyz = 1;
	pPass.bVertexColors = false;

#endif

#if %_LT_LIGHTS
  // Light types
  const int aLType[4] = {%_LT_0_TYPE, %_LT_1_TYPE, %_LT_2_TYPE, %_LT_3_TYPE};        
 #ifdef D3D10
  [unroll]
 #endif
  for (int i=0; i<%_LT_NUM; i++)
  {
    int nType = aLType[i];                              
    float4 WorldLightPos = LGetPosition(i);
    half4 Diffuse =  LGetDiffuse(i);
    half4 Specular;
      Specular = Diffuse;
      Specular.xyz *= Diffuse.w;


		half fOcclShadow;

    // Some optimisations for sun light (per-frame parameters and hardcoded values)    
    if (nType == LT_DIRECTIONAL)
    {
      WorldLightPos = g_PS_SunLightDir;
      fOcclShadow = 1.h - pPass.cNormalMapRT.w;
    }
		else
		{
			half4 ShadowChanMask = LGetShadowMask(i);
			fOcclShadow = saturate( 1 - dot(pPass.cShadowOcclMap, ShadowChanMask) );         // 1 alu
			// disable for now - looks very wrong with SRGB active
			// make shadowed areas more interesting
			//half fOcclShadow = saturate( 1 - dot(pPass.cShadowOcclMap, ShadowChanMask) + 0.05);         // 1 alu
			//if( pPass.bDisableInShadowShading  )
			//  fOcclShadow = saturate( 1 - dot(pPass.cShadowOcclMap, ShadowChanMask));                    // 1 alu
		}
        
    half fFallOff = 1;
    float3 vLight, vLightWS;        
    if (nType == LT_DIRECTIONAL)
    {      
      vLightWS = WorldLightPos.xyz * 10000.0f;
      vLight = WorldLightPos.xyz;
    }
    else
    {
      vLightWS = WorldLightPos.xyz - pPass.IN.vView.xyz;      
      vLight = normalize(vLightWS.xyz);                                                         // 3 alu
      fFallOff = GetAttenuation(vLightWS.xyz, WorldLightPos.w);                                   // 2 alu
    }       

	#if !%_RT_DEFERRED_SHADING
		if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_POM || pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_OBM)
    {
      float3 light = mul(pPass.mTangentToWS, vLight.xyz);
      float2 lightDelta = float2(light.x, light.y) * pPass.fBumpHeightScale;
      
      float h0 = GetBlendedHeight(pPass.IN.bumpTC.xy, pPass.blendFac, blendLayer2Tiling);
      float h = h0;

      if (pPass.nMicroDetailQuality == MICRO_DETAIL_QUALITY_POM)
      {
        h = max(0, GetBlendedHeight(pPass.IN.bumpTC.xy + 1.000 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.875 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.750 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.625 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.500 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.375 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.250 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.125 * lightDelta, pPass.blendFac, blendLayer2Tiling));
      }
      else
      {
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 1.000 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.666 * lightDelta, pPass.blendFac, blendLayer2Tiling));
        h = max(h, GetBlendedHeight(pPass.IN.bumpTC.xy + 0.333 * lightDelta, pPass.blendFac, blendLayer2Tiling));
      }
      float soften = pPass.fSelfShadowStrength;
      if(SnowVolumeParams[0].x)
		soften *= pPass.cSnowMap.w;
#if TEMP_TERRAIN
    	soften *= pPass.IN.Color.g;
#endif      
      float shadow = 1 - saturate((h - h0) * soften);
      
      fOcclShadow *= shadow;
    }
	#endif
        
    // Get projector filter color if needed
    half3 filterColor = 1;
		#if %_LT_HASPROJ 
      if (nType == LT_PROJECTED)
      {
       	float4 P = pPass.IN.projTC.xyzw;
        P.xy /= P.w;

        filterColor = tex2D(projMapSampler, P.xy ); // alpha contains luminance

	      float4 clipFrustum;
	      clipFrustum.xy = P.xy;
	      clipFrustum.zw = 1.0-P.xy;
#if !%_RT_AMBIENT
	      clip( clipFrustum );
        clip(P.w);
#endif

#if %_RT_AMBIENT
	      float4 b = (clipFrustum < 0.0);
	      float fBalance = dot(b, float4(1,1,1,1));
        fBalance += saturate(-sign(P.w));
        filterColor *= 1-saturate(fBalance);
#endif

        //filterColor = texCUBE(projCubeSampler, pPass.IN.projTC.xyz);
      }
  	#endif
    
    // Compute diffuse
    half fNdotL = dot(vLight.xyz, pPass.vNormal.xyz);                                                   // 1 alu
	
#if !%_RT_FOG

		// Skip SSDO for alpha blended passes, recursive, etc

	#if !XENON && !PS3 && !CAFE	
		// Directional occlusion using SH basis
		half4 occlusionTex = tex2Dproj(sceneNormalsBentSampler, pPass.IN.screenProj.xyzw);
		occlusionTex.xyz = (occlusionTex.xyz * 2.h - 1.h) * half3(0.48860, 0.48860, 0.48860);
		half fOcclusion = 1.h - saturate( dot( half4(vLight.xyz, 0.28209), occlusionTex ) * AlphaTest.z );
		fNdotL *= fOcclusion;
	#endif

#endif
  
    // Initialize light pass structure with per-light shared data
    fragLightPass pLight = (fragLightPass) 0;
    
    pLight.nType = nType;
                
    pLight.cDiffuse = Diffuse.xyz;      
    #if !%_RT_SHADER_LOD      
      pLight.cSpecular = Specular.xyz;     
    #endif

    pLight.vLight = vLight;
    
    pLight.fNdotL = fNdotL;
    pLight.fOcclShadow = fOcclShadow;
    
    pLight.fFallOff = fFallOff;
    pLight.cFilter = filterColor;
                                                                 
    // per shader custom shading 
    frag_custom_per_light(pPass, pLight);

    cOut.xyz += pLight.cOut.xyz;                                                                  // 1 alu
  }
#endif

  // Get ambient term
  frag_ambient( pPass, vAmbientNormal );

  if( pPass.bCustomComposition == false )
  {
    frag_final_composition( pPass, cOut.xyz );
  }
  
  frag_custom_end(pPass, cOut.xyz);
  
  if (pPass.bVertexColors)
  {
    cOut.xyz *= pPass.IN.Color.xyz;
  }
  
#if %_RT_AMBIENT
  if (pPass.bAlphaGlow)
  {
    cOut.xyz += pPass.cDiffuseMap.w * pPass.cDiffuseMap.xyz * pPass.fAlphaGlow_Multiplier;
  }
#endif

  // distance blend out
  cOut.w = pPass.fAlpha;

  // Setup final fog
  frag_fog_setup( pPass, cOut);

  // Setup hdr
  frag_hdr_setup( pPass, cOut);

  return cOut;
}

//============================================================================================

